﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace StunEngine.Effects
{
    /// <summary>
    /// Effect封装类的基类。
    /// </summary>
    public abstract class BaseEffectWrapper :IStunEngineEffect
    { 
        /// <summary>
        /// 类所包装的Effect类
        /// </summary>
        protected internal Effect effect;

        /// <summary>
        /// effect的参数集合
        /// </summary>
        protected internal EffectParameterCollection parameters;

        #region Effect参数

        protected EffectParameter worldParam;
        protected EffectParameter worldViewProjParam;
               
        protected EffectParameter eyePositionParam;
        protected EffectParameter clipPlaneEnabledParam;   
        protected EffectParameter clipPlaneParam;        
        
        #endregion

        #region 字段
        
        protected Matrix world = Matrix.Identity;
        protected Matrix view = Matrix.Identity;
        protected Matrix projection = Matrix.Identity;
        protected Matrix worldView;
        protected bool clipPlaneEnabled = false;

        protected internal EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;

        #endregion

        #region 属性

        /// <summary>
        /// 获取Effect中的Technique集合
        /// </summary>
        public EffectTechniqueCollection Techniques 
        {
            get{ return effect .Techniques; }
        }
        
        /// <summary>
        /// 获取或设置世界矩阵。世界矩阵改变时需要重新设置effect的World，WorldViewProj。
        /// </summary>
        public virtual Matrix World
        {
            get { return world; }
            set
            {
                world = value;
                dirtyFlags |= EffectDirtyFlags.World | EffectDirtyFlags.WorldViewProj;
            }
        }


        /// <summary>
        /// 获取或设置，视矩阵视矩阵改变时需要重新设置effect的WorldViewProj。
        /// </summary>
        public virtual Matrix View
        {
            get { return view; }
            set
            {
                view = value;
                dirtyFlags |= EffectDirtyFlags.WorldViewProj;
            }
        }

        /// <summary>
        /// 获取或设置投影矩阵，投影矩阵改变时需要重新设置effect的WorldViewProj。
        /// </summary>
        public virtual Matrix Projection
        {
            get { return projection; }
            set
            {
                projection = value;
                dirtyFlags |= EffectDirtyFlags.WorldViewProj;
            }
        }

        /// <summary>
        /// 获取或设置相机位置
        /// </summary>
        public virtual Vector3 EyePosition
        {
            get { return eyePositionParam.GetValueVector3 (); }
            set { eyePositionParam.SetValue (value);}
        }        

        /// <summary>
        /// 获取或设置剪裁平面，将这个值设置为Vector.Zero就关闭剪裁
        /// </summary>
        public Vector4 ClipPlane
        {
            get { return clipPlaneParam.GetValueVector4(); }
            set 
            {
                if (value == Vector4.Zero)
                    clipPlaneEnabled = false;
                else
                    clipPlaneEnabled = true;
                clipPlaneEnabledParam.SetValue(clipPlaneEnabled);
                clipPlaneParam.SetValue (value);  
            }
        }

        #endregion

        /// <summary>
        /// 创建一个Effect封装的基类
        /// </summary>
        /// <param name="setEffect">要封装的Effect</param>
        public BaseEffectWrapper(Effect setEffect)
        {
            if (setEffect == null)
            {
                throw new ArgumentNullException("setEffect");
            }
            effect = setEffect.Clone();
            parameters = effect.Parameters;
            CacheEffectParameters();
        }

        /// <summary>
        /// 在effect.Apply()方法发生前根据需要重新计算shader参数。
        /// </summary>
        public virtual void OnApply()
        {
            // 重新计算WorldViewProj组合矩阵
            if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
            {
                Matrix worldView = Matrix.Multiply(world, view);
                Matrix worldViewProj = Matrix.Multiply(worldView, projection);
                worldViewProjParam.SetValue(worldViewProj);                
                dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
            }
            if ((dirtyFlags & EffectDirtyFlags.World) != 0)
                worldParam.SetValue(world);
            
        }

        /// <summary>
        /// 缓存effect的参数。
        /// </summary>
        protected virtual void CacheEffectParameters()
        {
            worldParam = parameters["World"];
            worldViewProjParam = parameters["WorldViewProj"];
            eyePositionParam = parameters["EyePosition"];

            clipPlaneEnabledParam = parameters["ClipPlaneEnabled"];
            clipPlaneParam = parameters["ClipPlane"];            
        }
    }
}